package com.donald.videoHub.util;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.donald.videoHub.entity.Video;
import com.donald.videoHub.service.VideoService;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.File;
import java.text.DecimalFormat;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;


// 视频文件扫描器类，用于扫描指定目录中的视频文件并保存相关信息
public class VideoFileScanner {

    // 支持的视频文件扩展名列表
    private static final List<String> VIDEO_EXTENSIONS = Arrays.asList("mp4", "avi", "mov", "wmv", "mkv", "flv", "rmvb");

    // 线程池的线程数量，等于可用处理器数量加一
    private static final int NUM_THREADS = Runtime.getRuntime().availableProcessors() + 1;

    // 100KB
    public static final int HUNDRED_KB = 100000;

    // 1M
    public static final int MB = 1000000;

    // 10M
    public static final int TEN_MB = 10000000;

    // 100M
    public static final int HUNDRED_MB = 100000000;
    private static final Logger log = LogManager.getLogger(VideoFileScanner.class);

    // 创建线程池，核心线程数为NUM_THREADS，最大线程数为2 * NUM_THREADS，空闲线程存活时间为1000毫秒
    private ExecutorService executor = new ThreadPoolExecutor(NUM_THREADS, 2 * NUM_THREADS, 1000L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());

    // 原子计数器，用于记录扫描到的视频文件数量
    private AtomicLong num = new AtomicLong(0);

    // 原子计数器，用于记录扫描到的视频文件总大小
    private AtomicLong size = new AtomicLong(0);

    // 视频服务，用于处理视频文件的保存等操作
    private VideoService service;

    // 并发队列，用于存储扫描到的视频文件
    private ConcurrentLinkedDeque<Video> queue = new ConcurrentLinkedDeque<>();

    // 集合，用于存储所有已存在的视频文件大小
    private Set<Long> allSet = new HashSet<>();

    // 扫描指定目录，查找视频文件并处理
    public void scan(String directory) {
        File dir = new File(directory);
        String[] extensions = VIDEO_EXTENSIONS.toArray(new String[0]);
        try {
            // 列出目录下所有符合扩展名的视频文件
            List<File> files = (List<File>) FileUtils.listFiles(dir, extensions, true);
            files.stream().forEach(file -> {
                // 过滤小于10MB的文件和已存在的文件
                if (file.length() < HUNDRED_KB || allSet.contains(file.length())) return;

                // 增加计数器并输出日志信息，每100个文件记录一次
                long currentNum = num.incrementAndGet();
                log.info("第{}个:{} {}G 路径{}", currentNum, file.getName(), size.get() / 1024.0 / 1024 / 1024 / 1024, file.getAbsolutePath());
                // 计算文件大小（单位：GB）
                double sizeInGB = file.length() / 1024.0 / 1024 / 1024;
                this.size.addAndGet(file.length());

                // 创建Video对象并设置其属性
                Video video = new Video();
                video.setSizeB(file.length());
                video.setCreateTime(new Date());
                video.setTitle(file.getName());
                video.setPath(file.getAbsolutePath());
                video.setSize((new DecimalFormat("0.000")).format(sizeInGB));

                // 将Video对象添加到队列中
                queue.add(video);
            });
        } catch (Exception e) {
            // 捕获并记录异常
            // log.error("Error scanning directory {}: {}", directory, e.getMessage(), e);
        }
    }

    // 扫描计算机上的所有根目录
    public void scanComputer() {
        File[] roots = File.listRoots();
        for (File root : roots) {
            scanDirectory(root);
        }
    }

    // 获取当前计算机下磁盘盘路径
    public String[] getDiskPath() {
        File[] roots = File.listRoots();
        String[] paths = new String[roots.length];
        for (int i = 0; i < roots.length; i++) {
            paths[i] = roots[i].getAbsolutePath();
        }
        return paths;
    }

    // 递归扫描指定目录及其子目录
    public void scanDirectory(File directory) {
        if (!directory.canRead()) {
            return;
        }
        // 扫描当前目录
        scan(directory.getAbsolutePath());
        // 获取所有子目录并递归扫描
        File[] subdirectories = directory.listFiles(File::isDirectory);
        if (subdirectories != null) {
            Arrays.stream(subdirectories).forEach(dir -> executor.submit(() -> scanDirectory(dir)));
        }
    }

    // 开始扫描指定路径
    public String scanDirectory(String path) {
        long startTime = System.currentTimeMillis();
        log.info("Start scanning: " + path);

        // 从数据库中获取所有视频文件的大小并存入集合
        LambdaQueryWrapper<Video> wpr = new LambdaQueryWrapper<>();
        wpr.select(Video::getSizeB);
        allSet = service.list(wpr).stream().map(Video::getSizeB).collect(Collectors.toSet());

        // 判断是扫描整台计算机还是指定路径
        if (StringUtils.isBlank(path)) {
            scanComputer();
        } else {
            scanDirectory(new File(path));
        }

        // 关闭线程池并等待所有任务完成
        executor.shutdown();
        try {
            if (!executor.awaitTermination(1, TimeUnit.HOURS)) {
                executor.shutdownNow();
            }
        } catch (InterruptedException e) {
            log.error("Thread pool interrupted during shutdown: {}", e.getMessage(), e);
            executor.shutdownNow();
            Thread.currentThread().interrupt();
        }

        // 异步保存扫描结果
        Executors.newSingleThreadExecutor().submit(() -> {
            try {
                log.info("新增数量:{}", queue.size());
                service.saveBatch(queue);
            } catch (Exception e) {
                log.error("Error saving video batch: {}", e.getMessage(), e);
            }
        });

        // 输出扫描总耗时
        long endTime = System.currentTimeMillis();
        String result = String.format("路径%s 大小%.2fT 耗时%d秒", path, size.get() / 1024.0 / 1024 / 1024 / 1024.0, (endTime - startTime) / 1000);
        log.info(result);
        return result;
    }

    // 启动视频文件扫描器
    public static VideoFileScanner start(VideoService service) {
        return new VideoFileScanner(service);
    }

    // 视频文件扫描器构造函数，初始化视频服务
    public VideoFileScanner(VideoService service) {
        this.service = service;
    }

}
